package com.basker.pisces.exception;

import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.Arrays;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import com.basker.pisces.resource.StringResources;
import com.basker.pisces.utils.ReflectionUtils;

/**
 * 异常工厂
 *
 * @author hangwen
 */
public class ExceptionFactory {

    /**
     * 创建平台运行时异常
     *
     * @param exceptionType 抛出的异常类型
     * @param code          异常编码，通常是：系统.模块.类.方法-序号，如果在{@link StringResources}中存在该key,则异常消息为该key对应的值
     * @param paras         异常消息的参数
     * @return
     */
    public static RuntimeException createRuntimeException(Class<? extends ApplicationRuntimeException> exceptionType,
                                                          String code, Object... paras) {
        return createRuntimeException(exceptionType, null, code, paras);
    }

    /**
     * 创建平台运行时异常
     *
     * @param exceptionType 抛出的异常类型
     * @param cause         异常原因,异常的完整类名可以作为{@link StringResources}的key
     * @param paras         异常消息的参数
     * @return
     */
    public static RuntimeException createRuntimeException(Class<? extends ApplicationRuntimeException> exceptionType,
                                                          Throwable cause, Object... paras) {
        Assert.notNull(cause, "parameter 'cause' is required");
        return createRuntimeException(exceptionType, cause, cause.getClass().getName(), paras);
    }

    /**
     * 创建平台运行时异常,cause和code至少提供一个
     *
     * @param exceptionType 抛出的异常类型
     * @param cause         异常原因
     * @param code          异常编码，通常是：系统.模块.类.方法-序号，如果在{@link StringResources}中存在该key,则异常消息为该key对应的值
     * @param paras         异常消息的参数
     * @return
     */
    public static RuntimeException createRuntimeException(Class<? extends ApplicationRuntimeException> exceptionType,
                                                          Throwable cause, String code, Object... paras) {
        if (StringUtils.isEmpty(code) && cause == null) {
            throw new IllegalArgumentException("cause and code are both null!");
        }

        if (cause instanceof ApplicationRuntimeException) {
            return (RuntimeException) cause;
        }

        if (cause instanceof InvocationTargetException) {
            return createRuntimeException(((InvocationTargetException) cause).getTargetException());
        }

        if (!StringUtils.isEmpty(code)) {
            String message = getMessage(code);

            if (!StringUtils.isEmpty(message)) {
                message = MessageFormat.format(message, paras);
            } else if (paras.length > 0 && paras[0] instanceof String) {
                message = String.valueOf(paras[0]);

                paras = Arrays.copyOfRange(paras, 1, paras.length, Object[].class);
                message = MessageFormat.format(message, paras);
            }

            if (!StringUtils.isEmpty(message)) {
                return ineerCreate(exceptionType, code, message, cause);
            }

        }
        if (cause != null) {
            return ineerCreate(exceptionType, null, cause.getMessage(), cause);
        }

        return new ApplicationRuntimeException("UnkownException", "UnkownException");
    }

    /**
     * 创建平台运行时异常
     *
     * @param cause 反射过程中的异常
     * @return
     */
    public static RuntimeException createRuntimeException(InvocationTargetException cause) {
        Assert.notNull(cause, "parameter 'cause' is required");
        return createRuntimeException(cause.getTargetException());
    }

    /**
     * 创建平台运行时异常
     *
     * @param code  异常编码，通常是：系统.模块.类.方法-序号，如果在{@link StringResources}中存在该key,则异常消息为该key对应的值
     * @param paras 异常消息的参数
     * @return
     */
    public static RuntimeException createRuntimeException(String code, Object... paras) {
        Assert.hasText(code, "parameter 'code' is required");
        return createRuntimeException(ApplicationRuntimeException.class, null, code, paras);
    }

    /**
     * 创建平台运行时异常
     *
     * @param cause 异常原因，异常的完整类名可以作为{@link StringResources}的key
     * @param paras 异常消息的参数
     * @return
     */
    public static RuntimeException createRuntimeException(Throwable cause, Object... paras) {
        return createRuntimeException(ApplicationRuntimeException.class, cause, cause.getClass().getName(), paras);
    }

    /**
     * 创建平台运行时异常,cause和code至少提供一个
     *
     * @param cause 异常原因
     * @param code  异常编码，通常是：系统.模块.类.方法-序号，如果在{@link StringResources}中存在该key,则异常消息为该key对应的值
     * @param paras 异常消息的参数
     * @return
     */
    public static RuntimeException createRuntimeException(Throwable cause, String code, Object... paras) {
        return createRuntimeException(ApplicationRuntimeException.class, cause, code, paras);
    }

    private static String getMessage(String code) {
        return StringResources.get(code);
    }

    private static ApplicationRuntimeException ineerCreate(Class<? extends ApplicationRuntimeException> exceptionType,
                                                           String code, String message, Throwable cause) {

        if (exceptionType == null) {
            return new ApplicationRuntimeException(code, message, cause);
        }

        try {
            if (cause == null) {
                return ReflectionUtils.createNewInstance(exceptionType, new Class[]{String.class, String.class},
                        code, message);
            } else {
                return ReflectionUtils.createNewInstance(exceptionType,
                        new Class[]{String.class, String.class, Throwable.class}, code, message, cause);
            }
        } catch (Exception e) {
            return new ApplicationRuntimeException(code, message, cause);
        }

    }

}